using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using EARTHLib;

namespace GoogleEarthCOMWrapper
{
    #region GoogleEarth Specific Exceptions
    public class ApplicationUninitializedException : ApplicationException
    {
        public ApplicationUninitializedException(string message, Exception innerException)
            : base(message, innerException)
        {
        }
    }
    public class InvalidOrDeletedFeatureException : ApplicationException
    {
        public InvalidOrDeletedFeatureException(string message, Exception innerException)
            : base(message, innerException)
        {
        }
    }
    public class FeatureHasNoViewException : ApplicationException
    {
        public FeatureHasNoViewException(string message, Exception innerException)
            : base(message, innerException)
        {
        }
    }
    #endregion

    public class GoogleEarthAPI
    {
        #region Private Members
        private ApplicationGEClass _applicationGEClass;
        #endregion

        #region Constructor / Init
        public GoogleEarthAPI()
        {
            this._applicationGEClass = new ApplicationGEClass();

            while (_applicationGEClass.IsInitialized() == 0)
            {
                // Give the app a couple seconds to Load
                Console.WriteLine("Waiting for Google Earth to initialize...");
                Thread.Sleep(500);
            }
        }
        #endregion

        #region Public Member Functions
        public CameraInfoGEClass GetCamera(bool considerTerrain)
        {
            try
            {
                return _applicationGEClass.GetCamera(considerTerrain ? 1 : 0) as CameraInfoGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }

            return null;
        }

        public void SetCamera(CameraInfoGEClass cameraInfo, double speed)
        {
            try
            {
                _applicationGEClass.SetCamera(cameraInfo, speed);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public void SetCameraParams(double lat, double lon, double alt, AltitudeModeGE altMode, double range,
            double tilt, double azimuth, double speed)
        {
            try
            {
                _applicationGEClass.SetCameraParams(lat, lon, alt, altMode, range, tilt, azimuth, speed);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public void SaveScreenShot(string fileName, int quality)
        {
            try
            {
                _applicationGEClass.SaveScreenShot(fileName, quality);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }
        
        public void OpenKmlFile(string fileName, bool suppressMessages)
        {
            try
            {
                _applicationGEClass.OpenKmlFile(fileName, suppressMessages ? 1 : 0);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public void LoadKmlData(string data)
        {
            try
            {
                _applicationGEClass.LoadKmlData(ref data);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public FeatureGEClass GetFeatureByName(string name)
        {
            try
            {
                return _applicationGEClass.GetFeatureByName(name) as FeatureGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }

            return null;
        }

        public FeatureGEClass GetFeatureByHref(string href)
        {
            try
            {
                return _applicationGEClass.GetFeatureByHref(href) as FeatureGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }

            return null;
        }

        public void SetFeatureView(FeatureGEClass feature, double speed)
        {
            try
            {
                _applicationGEClass.SetFeatureView(feature, speed);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public GeoPoint GetPointOnTerrainFromScreenCoords(double screen_x, double screen_y)
        {
            try
            {
                Array ret = _applicationGEClass.GetPointOnTerrainFromScreenCoords(screen_x, screen_y);
                GeoPoint gp = new GeoPoint((double)ret.GetValue(0), (double)ret.GetValue(1), (double)ret.GetValue(2));
                return gp;
            }
            catch(Exception e)
            {
                ThrowException(e);
            }

            return new GeoPoint(0, 0, 0);
        }

        public bool IsInitialized()
        {
            try
            {
                return (_applicationGEClass.IsInitialized() == 0) ? false : true;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return false;
        }

        public bool IsOnline()
        {
            try
            {
                return (_applicationGEClass.IsOnline() == 0) ? false : true;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return false;
        }

        public void Login()
        {
            try
            {
                _applicationGEClass.Login();
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public void Logout()
        {
            try
            {
                _applicationGEClass.Logout();
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public void ShowDescriptionBalloon(FeatureGEClass feature)
        {
            try
            {
                _applicationGEClass.ShowDescriptionBalloon(feature);
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public void HideDescriptionBalloons()
        {
            try
            {
                _applicationGEClass.HideDescriptionBalloons();
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
        }

        public FeatureGEClass GetHighlightedFeature()
        {
            try
            {
                return _applicationGEClass.GetHighlightedFeature() as FeatureGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return null;
        }

        public FeatureGEClass GetMyPlaces()
        {
            try
            {
               return _applicationGEClass.GetMyPlaces() as FeatureGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return null;
        }

        public FeatureGEClass GetTemporaryPlaces()
        {
            try
            {
                return _applicationGEClass.GetTemporaryPlaces() as FeatureGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return null;
        }

        public FeatureCollectionGEClass GetLayersDatabases()
        {
            try
            {
                return _applicationGEClass.GetLayersDatabases() as FeatureCollectionGEClass;
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return null;
        }

        public int GetMainHwnd()
        {
            try
            {
                return _applicationGEClass.GetMainHwnd();
            }
            catch (Exception e)
            {
                ThrowException(e);
            }
            return 0;
        }
        #endregion

        #region Public Properties
        public int StreamingProgressPercentage
        {
            get { return _applicationGEClass.StreamingProgressPercentage; }
        }

        public double AutoPilotSpeed
        {
            get { return _applicationGEClass.AutoPilotSpeed; }
            set { _applicationGEClass.AutoPilotSpeed = value; }
        }

        public ViewExtentsGEClass ViewExtents
        {
            get { return _applicationGEClass.ViewExtents as ViewExtentsGEClass; }
        }

        public int VersionMajor
        {
            get { return _applicationGEClass.VersionMajor; }
        }

        public int VersionMinor
        {
            get { return _applicationGEClass.VersionMinor; }
        }

        public int VersionBuild
        {
            get { return _applicationGEClass.VersionBuild; }
        }

        public AppTypeGE VersionAppType
        {
            get { return _applicationGEClass.VersionAppType; }
        }

        public double ElevationExaggeration
        {
            get { return _applicationGEClass.ElevationExaggeration; }
            set { _applicationGEClass.ElevationExaggeration = value; }
        }
        #endregion

        #region Private Helpers
        private void ThrowException(Exception e)
        {
            string message = "Unknown Exception";
            Exception ret = new Exception(message);

            if (e is System.Runtime.InteropServices.COMException)
            {
                uint errorCode = (uint)((System.Runtime.InteropServices.COMException)e).ErrorCode;
                switch (errorCode)
                {
                    case 0x80048002:
                        message = "GoogleEarth not initilaized";
                        ret = new ApplicationUninitializedException(message, e);
                        break;
                    case 0x80048004:
                        message = "Feature has no view";
                        ret = new FeatureHasNoViewException(message, e);
                        break;
                    case 0x80048001:
                        message = "Invalid or deleted feature";
                        ret = new InvalidOrDeletedFeatureException(message, e);
                        break;
                }
            }

            throw ret;
        }
        #endregion
    }
}
